package entities.role;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import entities.concept.AndConcept;
import entities.concept.Concept;

/**
 * A general role path is a sequence of concepts and roles. 
 * @author j55wu
 *
 */
public class GeneralRolePath {
	private List<Object> sequence;
	//offset ++ when a role is removed, -- when a role is added.
	private int offset;
	
	public GeneralRolePath(){
		this.sequence = new ArrayList<Object>();
		this.offset = 0;
	}
	
	public List<Object> getContent(){
		return this.sequence;
	}
	public int getOffset(){
		return this.offset;
	}
	public Object getLastItem(){
		int idx = this.sequence.size();
		if(idx>0){
			return this.sequence.get(idx-1);
		}
		return null;
	}
	
	public Role getOldestRole(){
		int i =this.getOldestRoleIndex();
		if(i<0){
			return null;
		}
		return (Role) this.sequence.get(i);
	}
	
	public Role getLastRole(){
		int i =this.getLastRoleIndex();
		if(i<0){
			return null;
		}
		return (Role) this.sequence.get(i);
	}
	
	/**
	 * Remove the sublist from the beginning of the list (index=0, inclusive) to this index(inclusive)
	 * @param index
	 * @return A concept generated from the sublist without the ending role
	 */
	public Concept removeContentToIndex(int index){
		List<Object> removed = this.sequence.subList(0, index+1);
		//the last one is the role, not to be included in the concept
		Concept c = null;
		switch(removed.size()){
			case 0:
			case 1: c = null; break;
			case 2: c = (Concept) removed.get(0); break;
			default:
				HashSet<Concept> cons = new HashSet<Concept>();
				for(int i=0; i<removed.size()-1; i++){
					cons.add((Concept) removed.get(i));
				}
				c = new AndConcept(cons); break;
		}
		removed.clear();
		return c;
	}
	
	/**
	 * remove contents from the specified index to the end of the list (both ends inclusive)
	 * @param index
	 * @return a concept generated from the index (exclusive) to the end
	 * 
	 */
	public Concept removeContentToBottom(int index){
		List<Object> removed = this.sequence.subList(index, this.sequence.size());
		//the first one is the returned role, not included in the concept
		Concept c = null;
		switch(removed.size()){
			case 0:
			case 1: c = null; break;
			case 2: c = (Concept) removed.get(1); break;
			default:
				HashSet<Concept> cons = new HashSet<Concept>();
				for(int i=1; i<removed.size(); i++){
					cons.add((Concept) removed.get(i));
				}
				c = new AndConcept(cons); break;
		}
		removed.clear();
		return c;
	}
	
	public int getLastRoleIndex(){
		for(int i=this.sequence.size()-1; i>=0; i--){
			Object item = this.sequence.get(i);
			if(item instanceof Role){
				return i;
			}
		}
		return -1;
	}
	
	public int getOldestRoleIndex(){
		for(int i=0; i<this.sequence.size(); i++){
			Object item = this.sequence.get(i);
			if(item instanceof Role){
				return i;
			}
		}
		return -1;
	}
	
	public boolean hasRoles(){
		for(Object o:this.sequence){
			if(o instanceof Role){
				return true;
			}
		}
		return false;
	}
	
	public void addContent(Object o){
		this.sequence.add(o);
		if(o instanceof Role){
			this.offset--;
		}
		//add a concept c, possible c/\d
//		Object last = this.sequence.get(this.sequence.size()-1);
//		if(last instanceof Concept && o instanceof Concept){
//			AndConcept ac = new AndConcept((Concept)last, (Concept)o);
//			this.sequence.remove(this.sequence.size()-1);
//			this.sequence.add(ac);
//		}
	}
	
	public void rmContent(int index){
		//remove the specified index of the list
		if(this.sequence.get(index) instanceof Role){
			this.offset++;
		}
		this.sequence.remove(index);
	}
	
	public int length(){
		return this.sequence.size();
	}
	
	public GeneralRolePath newCopy(){
		GeneralRolePath cp = new GeneralRolePath();
		cp.sequence.addAll(this.sequence);
		cp.offset = this.offset;
		return cp;
	}
	@Override
	public String toString(){
		String st = this.offset+"<";
		for(Object o:this.sequence){
			st += o.toString()+",";
		}
		return st+">";
	}

	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + offset;
		result = prime * result
				+ ((sequence == null) ? 0 : sequence.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof GeneralRolePath))
			return false;
		GeneralRolePath other = (GeneralRolePath) obj;
		if (offset != other.offset)
			return false;
		if (sequence == null) {
			if (other.sequence != null)
				return false;
		} else if (!sequence.equals(other.sequence))
			return false;
		return true;
	}
	
	
	
}
